package com.spzx.common.security.aspect;

import com.spzx.common.security.annotation.GuiGuCache;
import com.spzx.common.security.annotation.InnerAuth;
import lombok.SneakyThrows;
import lombok.extern.slf4j.Slf4j;
import org.aspectj.lang.ProceedingJoinPoint;
import org.aspectj.lang.annotation.Around;
import org.aspectj.lang.annotation.Aspect;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.data.redis.core.RedisTemplate;
import org.springframework.data.redis.core.script.DefaultRedisScript;
import org.springframework.stereotype.Component;

import java.util.Arrays;
import java.util.UUID;
import java.util.concurrent.TimeUnit;
import java.util.stream.Collectors;

/**
 * ClassName: GuiGuCacheAspect
 * Package: com.spzx.common.security.aspect
 * Description:
 */
@Aspect
@Component
@Slf4j
public class GuiGuCacheAspect {
    @Autowired
    private RedisTemplate redisTemplate;

    @Around("@annotation(guiGuCache)")
    @SneakyThrows
    public Object guiGuCacheAdvice(ProceedingJoinPoint point, GuiGuCache guiGuCache) {
        try {
            //定义key
            String prefix = guiGuCache.prefix();
            String paramVal = null;
            Object[] args = point.getArgs();
            if (args != null && args.length > 0) {
                paramVal = Arrays.asList(args).stream().map(arg -> arg.toString()).collect(Collectors.joining(":"));
            }
            String dataKey = prefix + paramVal;

            //查看缓存中是否有这个数据
            Object result =  redisTemplate.opsForValue().get(dataKey);
            if (result != null) {
                log.info("命中缓存直接返回,线程名:{},线程id:{}", Thread.currentThread().getName(), Thread.currentThread().getId());
                return result;
            }

            //缓存中没有则使用分布式锁在数据库中查询
            String LockKey = dataKey + "lock";
            String LockValue = UUID.randomUUID().toString().replaceAll("-", "");
            Boolean flag = redisTemplate.opsForValue().setIfAbsent(LockKey, LockValue, 3, TimeUnit.SECONDS);
            if (flag) {
                try {
                    //获取到锁
                    log.info("获取锁成功：线程名:{},线程id:{}", Thread.currentThread().getId(), Thread.currentThread().getName());
                    result = point.proceed();
                    long ttl = result == null ? 1 * 60 : 5 * 60;
                    redisTemplate.opsForValue().set(dataKey, result, ttl, TimeUnit.SECONDS);
                    return result;
                } finally {
                    String script = """
                            if 
                                redis.call('get', KEYS[1]) == ARGV[1] 
                            then 
                                return redis.call('del', KEYS[1]) 
                            else 
                                return 0 
                            end
                            """;
                    redisTemplate.execute(new DefaultRedisScript(script, Boolean.class), Arrays.asList(LockKey), LockValue);

                }
            } else {
                //没获取到锁
                log.info("获取锁失败,自旋：线程名:{},线程id:{}", Thread.currentThread().getId(), Thread.currentThread().getName());
                try {
                    Thread.sleep(100);
                } catch (InterruptedException e) {
                    throw new RuntimeException(e);
                }
                return this.guiGuCacheAdvice(point,guiGuCache);
            }
        } catch (RuntimeException e) {
            log.error("[商品服务]查询商品信息异常:{}", e);
            return point.proceed();
        }
    }
}
